package util.core;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import util.input.Parser;

import java.util.StringTokenizer;

public class Atom {

    private int ATOMIC_NUMBER, GROUP;
    private double ATOMIC_WEIGHT, ELECTRONEGATIVITY, IONIZATION_POTENTIAL, BOILING_POINT, MELTING_POINT;
    private int[] OXIDATION_STATES;
    private String SYMBOL, NAME, CONFIGURATION;

    public double getAtomicWeight() {
        return ATOMIC_WEIGHT;
    }

    public int getAtomicNumber() {
        return ATOMIC_NUMBER;
    }

    public String getSymbol() {
        return SYMBOL;
    }

    public String getName() {
        return NAME;
    }

    /**
     * Constructs a new Atom based on the node. Note that this method should never be called.
     *
     * @param fstNode The Node which contains the info on the element.
     */
    public Atom(Node fstNode) {
        SYMBOL = extract("SYMBOL", fstNode);
        ATOMIC_WEIGHT = Double.parseDouble(extract("ATOMIC_WEIGHT", fstNode));
        ATOMIC_NUMBER = Integer.parseInt(extract("ATOMIC_NUMBER", fstNode));
        String electronegativity = extract("ELECTRONEGATIVITY", fstNode);
        String ionization = extract("IONIZATION_POTENTIAL", fstNode);
        String boilingPoint = extract("BOILING_POINT", fstNode);
        String meltingPoint = extract("MELTING_POINT", fstNode);
        CONFIGURATION = extract("ELECTRON_CONFIGURATION", fstNode);
        if (electronegativity != null)
            ELECTRONEGATIVITY = Double.parseDouble(electronegativity);
        if (ionization != null)
            IONIZATION_POTENTIAL = Double.parseDouble(ionization);
        else
            IONIZATION_POTENTIAL = -1;
        if (boilingPoint != null)
            BOILING_POINT = Double.parseDouble(boilingPoint);
        else
            BOILING_POINT = Double.MIN_VALUE;
        if (meltingPoint != null)
            MELTING_POINT = Double.parseDouble(meltingPoint);
        else
            MELTING_POINT = Double.MIN_VALUE;
        NAME = extract("NAME", fstNode);
        setGroup();
        setOxidation(fstNode);
    }

    private String extract(String tag, Node current) {
        try {
            Element a = (Element) current;
            NodeList b = a.getElementsByTagName(tag);
            a = (Element) b.item(0);
            b = a.getChildNodes();
            return b.item(0).getNodeValue();
        } catch (NullPointerException e) {
            return null;
        }
    }

    private void setOxidation(Node fstNode) {
        String k = extract("OXIDATION_STATES", fstNode);
        if (k == null)
            return;
        String temp;
        boolean plusMinus = false;
        int value;
        int count = 0;

        OXIDATION_STATES = new int[10];

        StringTokenizer st = new StringTokenizer(k, ",");
        while (st.hasMoreTokens()) {
            temp = st.nextToken().trim();
            if (temp.contains("+/-")) {
                temp = temp.replace("+/-", "");
                plusMinus = true;
            }
            value = Integer.parseInt(temp);
            OXIDATION_STATES[count] = value;
            count++;
            if (plusMinus) {
                OXIDATION_STATES[count] = -value;
                count++;
                plusMinus = false;
            }

        }

    }

    private void setGroup() {
        int k = getAtomicNumber();
        if (k > 54) {
            GROUP = k % 32 - 4;
            if (GROUP > 12 && GROUP < 21) {
                GROUP %= 18;
            } else if ((k > 57 && k < 72) || (k > 89 && k < 104)) {
                GROUP = -1;
            } else {
                GROUP %= 18;
            }
        } else if (k < 55 && k > 18) {
            GROUP = k % 18;
        } else if (k < 13 && k > 1) {
            GROUP = (k % 8 + 16) % 18;
        } else {
            GROUP = ATOMIC_NUMBER;
        }

        if (GROUP == 0) {
            GROUP = 18;
        }
    }

    public int hashCode() {
        return SYMBOL.hashCode();
    }

    @Override
    public boolean equals(Object o) {
        return (o instanceof Atom) && ((Atom) o).getSymbol().equals(SYMBOL);
    }

    @Override
    public String toString() {
        return SYMBOL;
    }

    public String info() {
        String s = "<html><small>";
        s += formatString("Name").substring(10) + NAME;
        s += formatString("Symbol") + SYMBOL;
        s += formatString("Atomic Number") + ATOMIC_NUMBER;
        s += formatString("Atomic Weight") + ATOMIC_WEIGHT;
        if (GROUP != -1)
            s += formatString("Group") + GROUP;
        else
            s += formatString("Group") + "N/A";
        if (OXIDATION_STATES == null)
            s += formatString("Oxidation States") + "N/A";
        else
            s += oxidationString();
        if (ELECTRONEGATIVITY != 0)
            s += formatString("Electronegativity") + ELECTRONEGATIVITY;
        if (IONIZATION_POTENTIAL != -1)
            s += formatString("Ionization Potential") + IONIZATION_POTENTIAL;
        if (BOILING_POINT != Double.MIN_VALUE)
            s += formatString("Boiling Point") + BOILING_POINT + "°K";
        if (MELTING_POINT != Double.MIN_VALUE)
            s += formatString("Melting Point") + MELTING_POINT + "°K";
        s += configurationString();
        return s + "</small></html>";
    }

    private String formatString(String s) {
        return "<br/><br/><u>" + s + ":</u><span>&nbsp</span>";
    }

    private String configurationString() {
        int i = CONFIGURATION.indexOf("]");
        String returnString = formatString("Electron Config") +
                CONFIGURATION.substring(0, i + 1) + "<span>&nbsp</span>";
        i += 2;
        for (; i < CONFIGURATION.length(); i++) {
            int end = Parser.findEndNum(CONFIGURATION, i);
            returnString += CONFIGURATION.substring(i, end + 1);
            i = end + 1;
            end = Parser.findEndNum(CONFIGURATION, i);
            returnString += "<sup><small>" + CONFIGURATION.substring(i, end) + "</small></sup><span>&nbsp</span>";
            i = end;
        }
        return returnString;
    }

    private String oxidationString() {
        String s = Integer.toString(OXIDATION_STATES[0]);
        int i;
        for (i = 1; i < 10 && OXIDATION_STATES[i] != 0; i++)
            s += ", " + OXIDATION_STATES[i];
        if (i == 1)
            return formatString("Oxidation State") + s;
        else
            return formatString("Oxidation States") + s;
    }

}
